library(tectr)
library(tidyverse)
library(haven)
library(magrittr)
library(glue)
devtools::load_all()
Loading vdem.tectr

Overview

Between the end of “Codebook_metaframe” and here, the metaframe may be changed by following the path to the specific json file. Now, the metaframe and the data will be created. We will begin with the basics of the metaframe, followed by the data, followed by further tweaking of the metaframe.

Metaframe

Import

I will now read mf_revisable to get the data from the json files:

tmp_mf_vdem <- fx_read(mf_revisable)
tmp_mf_vdem

I read in the data, as well:

path <- system.file("inst", "extdata", "Country_Year_V-Dem_STATA_v8", "V-Dem-CY-v8.dta",package = "vdem.tectr")
tmp_vdem <- read_dta(path)
tmp_vdem
rm(path)

Identifiers

haven::as_factor allows us to transform the appropriate columns into factors by applying the function to tmp_vdem. However, this method cannot distinguish between nominal and ordinal variables and we will therefore have to wait with this. First, I will add the identifier variables to the metaframe. I did not add them to the metaframe beforehand because their format is different and the information contained in the codebook mostly refers to other documents. It may sensible to add the information in a future version. The ones that I will explicitly add for now, are “country_name” and “year”.

tmp_mf_vdem <- bind_rows(tibble(name = c("country_name", "year"), 
                                fxInfo_name = c("Country Name", "Year")), 
                         tmp_mf_vdem)

Many of the columns of tmp_mf_vdem are not important for the task of formatting the data. The goal is, at first, to have a metaframe which contains all columns of the corresponding dataset (possibly more) and a dataframe where the values are correctly coded. We thus first look at the class of the columns of tmp_vdem:

tmp_vdem %>% 
  map_chr(class) %>% 
  table
.
character      Date  labelled   numeric 
       14         1       171      2506 

The only task is to differentiate between ordinal and nominal variables and make sure that all variables but the identifiers are described in the mf_vdem data. The identifiers are:

names(tmp_vdem)[1:21]
 [1] "country_name"        "country_text_id"     "country_id"          "year"                "historical_date"     "project"            
 [7] "historical"          "histname"            "codingstart"         "codingend"           "codingstart_contemp" "codingend_contemp"  
[13] "codingstart_hist"    "codingend_hist"      "gapstart1"           "gapstart2"           "gapstart3"           "gapend1"            
[19] "gapend2"             "gapend3"             "COWcode"            

Only “country_name” and “year” are contained in the metaframe.

idents <- names(tmp_vdem)[c(2, 3, 5:21)]

If we remove these variables, we get:

tmp_vdem %>% 
  select(-!!idents) %>% 
  map_chr(class) %>% 
  table
.
character  labelled   numeric 
       12       171      2490 

Preliminaries

Let’s take a peek at the column names that are not yet contained:

names(tmp_vdem %>% select(-!!idents)) %>% 
  extract(!(. %in% tmp_mf_vdem$name)) %>% 
  length
[1] 2428

There are three main reasons why so many variables are not contained in the metaframe:

  • there are series of dichotomous variables that are encoded by “_" for each level, e. g., v2csanmvch:
names(tmp_vdem) %>% str_subset(coll("v2csanmvch"))
 [1] "v2csanmvch_0"  "v2csanmvch_1"  "v2csanmvch_10" "v2csanmvch_11" "v2csanmvch_12" "v2csanmvch_2"  "v2csanmvch_3"  "v2csanmvch_4" 
 [9] "v2csanmvch_5"  "v2csanmvch_6"  "v2csanmvch_7"  "v2csanmvch_8"  "v2csanmvch_9" 
  • many names are saved in the form “, *_osp, *_ord"
  • there is additional information on many variables in the form of confidence intervals (“_codehigh“/”_codelow“), standard deviations (”_sd“) and the number of experts who coded them

As this is partly stacked in the direction in which I have listed it, we will decode it in this direction. However, there are two previous tasks to fulfill:

Column name corrections

There are a few variables that are inconsistently named.

names(tmp_vdem) %>% str_subset("osp_ex")
 [1] "v2elmulpar_osp_ex"    "v2elrgstry_osp_ex"    "v2elvotbuy_osp_ex"    "v2elirreg_osp_ex"     "v2elintim_osp_ex"    
 [6] "v2elpeace_osp_ex"     "v2elpeace_rec_osp_ex" "v2elboycot_osp_ex"    "v2elfrcamp_osp_ex"    "v2elpdcamp_osp_ex"   
[11] "v2elpaidig_osp_ex"    "v2elfrfair_osp_ex"    "v2elaccept_osp_ex"    "v2elasmoff_osp_ex"    "v3elbalpap_osp_ex"   
[16] "v3elbalstat_osp_ex"  

The variable name here is “_ex" and a more consistent name would therefore be “_ex_osp“. Let us look at the different forms in which these inconsistent variables come:

names(tmp_vdem) %>% str_subset("v2elmulpar_\\S*_(?:ex|leg)")
 [1] "v2elmulpar_codehigh_ex"      "v2elmulpar_codehigh_leg"     "v2elmulpar_codelow_ex"       "v2elmulpar_codelow_leg"     
 [5] "v2elmulpar_ord_codehigh_ex"  "v2elmulpar_ord_codehigh_leg" "v2elmulpar_ord_codelow_ex"   "v2elmulpar_ord_codelow_leg" 
 [9] "v2elmulpar_ord_ex"           "v2elmulpar_ord_leg"          "v2elmulpar_osp_codehigh_ex"  "v2elmulpar_osp_codehigh_leg"
[13] "v2elmulpar_osp_codelow_ex"   "v2elmulpar_osp_codelow_leg"  "v2elmulpar_osp_ex"           "v2elmulpar_osp_leg"         

We therefore define a function which corrects these names by switching the pattern with the suffix:

correct_names <- function(names) {
  # One of the following mixtures has to exist that is not empty:
  grid <- expand.grid(
    c("_osp", "_ord", ""),
    c("_codehigh", "_codelow", "_sd", "")
  )
  proper_end <- paste0(grid[[1]], grid[[2]]) %>%
    magrittr::extract(. != "") %>%
    paste(collapse = "|")
  unproblematic_pattern <- glue::glue("(?:{proper_end})$")
  long_grid <- expand.grid(
    c("_osp", "_ord"),
    c("_codehigh", "_codelow", "_sd")
  )
  long_proper_end <- paste0(long_grid[[1]], long_grid[[2]], collapse = "|")
  short_proper_end <- paste0(c("_osp", "_ord", "_codehigh", "_codelow", "_sd"), 
                             collapse = "|")
  names <- names %>% {
    if_else(
      str_detect(., unproblematic_pattern), ., 
      str_replace(., glue(
        "(<long_proper_end>)(_[:alpha:]{1,50})$", .open = "<", .close = ">"
        ), "\\2\\1")
    )
  } %>% {
    if_else(
      str_detect(., unproblematic_pattern), ., 
      str_replace(., glue(
        "(<short_proper_end>)(_[:alpha:]{1,50})$", .open = "<", .close = ">"
        ), "\\2\\1")
    )
  }
  names
}

I have included a short example with different flavors of correct and incorrect names:

ex <- c(
    "v2psprbrch_ord_codehigh",
    "v2elasmoff_codelow_ex",
    "v2elasmoff",
    "v2elasmoff_ord_codelow_ex",
    "v2elasmoff_ex", 
    "v2elpeace_rec_codelow_ex"
  )
correct_names(ex)
[1] "v2psprbrch_ord_codehigh"   "v2elasmoff_ex_codelow"     "v2elasmoff"                "v2elasmoff_ex_ord_codelow"
[5] "v2elasmoff_ex"             "v2elpeace_rec_ex_codelow" 

We now change tmp_vdem:

names(tmp_vdem) <- correct_names(names(tmp_vdem))
names(tmp_vdem) %>% str_subset("osp_ex")
character(0)

Metaframe name corrections

There are two reasons why names are incorrect in the metaframe: there is the pattern “, *_osp …" and “_3C/_4C …"

Whereas we only delete “, *_osp …“, the second pattern actually implies several variables and we therefore have to conduct an inner join (together with some more flexible regular expressions because of typos):

key <- dplyr::tibble(
    before = tmp_mf_vdem$name,
    name = tmp_mf_vdem$name %>%
      purrr::map(
      function(name) {
        if(stringr::str_detect(name, "\\*_osp,")) {
          stringr::str_extract(name, "^[^\\s*]*(?=,)") %>%
            return()
        }
        else if(stringr::str_detect(name, "_3C\\s\\/")) {
          stringr::str_extract(name, "^\\S*(?=_3C)") %>%
            paste0(c("_3C", "_4C", "_5C")) %>%
            return()
        }
        else return(name)
      }
    )
  ) %>% tidyr::unnest()
tmp_mf_vdem <- 
  dplyr::inner_join(key, tmp_mf_vdem, by = c(before = "name")) %>%
  dplyr::select(-before)

Split the series of dichotomous variables

Besides extending the names, the following code edits the question and answer of the new dichotomous variable so that they are more appropriate. Because of some inconsistencies in the codebook, I had to try out some different criteria for dichotomous variables. The following ones turned out to be appropriate:

is_ds <- tmp_mf_vdem %$%
  {((stringr::str_detect(fxInfo_answer_type, "(?:S|s)election") &
    !stringr::str_detect(fxInfo_scale, "Ordinal")) |
    stringr::str_detect(fxInfo_scale, "(?:S|s)eries")) & 
    !(name %in% c("country_name", "year", "v2expathhg"))}
mf_rem <- tmp_mf_vdem[!is_ds, ]
mf_dich <- tmp_mf_vdem[is_ds, ]
mf_dich_new <- seq_len(nrow(mf_dich)) %>%
  purrr::map_dfr(
    function(i_row) {
      tmp_old <- mf_dich[i_row, ]
      resps <- tmp_old$fxInfo_responses[[1]] %>%
        dplyr::as_tibble() %>%
        dplyr::mutate(
          value = stringr::str_split_fixed(value,
              pattern = stringr::coll("(0=No, 1=Yes)"),
              n = 2)[, 1] %>% stringr::str_trim()
        )
      question <- tmp_old$fxInfo_question
      question_new <- glue::glue(
        question, " Is the answer \"{resps$value}\"?"
      ) %>% as.character()
      response_new <- "Yes or No"
      name_new <- paste0(tmp_old$name, "_", resps$key)
      tmp_old <- tmp_old[rep(1, nrow(resps)), ]
      tmp_old %>%
        dplyr::mutate(
          name = name_new,
          fxInfo_question = question_new,
          fxInfo_responses = list(response_new)
        )
    }
  )
tmp_mf_vdem <- dplyr::bind_rows(mf_rem, mf_dich_new)

Only keep variables that can be found in tmp_vdem

While the metaframe might be extended in future versions, for now, it seems sensible to retain only the columns that can be found in tmp_vdem. To make sure that we did not overlook anything, we will do this gradually.

First of all, those variables of that are found in the fourth and fifth part are only present in the extended dataset:

tmp_mf_vdem %>%
  filter(part_num >= 4) %$% 
  name %in% names(tmp_vdem) %>% 
  any
[1] FALSE

We therefore remove them:

tmp_mf_vdem <- tmp_mf_vdem %>% 
  filter(part_num < 4)

Furthermore, many sections contain introduction which, again, might be interesting in the future but which we will remove for now:

tmp_mf_vdem %>%
  filter(str_detect(name, "intro") | str_detect(fxInfo_name, "comment")) %$% 
  name %in% names(tmp_vdem) %>% 
  any
[1] FALSE
tmp_mf_vdem <- tmp_mf_vdem %>% 
  filter(!str_detect(name, "intro") & !str_detect(fxInfo_name, "comment"))

Some variables can only be found in the disaggregated dataset:

tmp_mf_vdem %>%
  filter(str_detect(fxInfo_data_release, "disaggregated dataset")) %$% 
  name %in% names(tmp_vdem) %>% 
  any
[1] FALSE
tmp_mf_vdem <- tmp_mf_vdem %>%
  filter(!str_detect(fxInfo_data_release, "disaggregated dataset"))

Furthermore, the cautionary notes report some variables that have been excluded:

tmp_mf_vdem <- tmp_mf_vdem %>% 
  filter(!(name %in% c(
    "v2elnoncit", "v2elmalsuf", "v2elfemsuf", "v2elmalsuf_ex", "v2elfemsuf_ex",
    "v2elmalsuf_leg", "v2elfemsuf_leg", "v2elsnlpop", "v2elsnmpop", 
    "v2psswitch", "v2clsnmpct", "v2svstterr", "v2svstpop", "v2meaccess", 
    "v2lgqumin"
  )))

We will inspect the remaining variables that do not exist, manually:

print(filter(tmp_mf_vdem, !(name %in% names(tmp_vdem))), n = 40)
  • The variables from the contemporary V-DEM (beginning with v2) have previous data releases associated with them, except for “v2eladltvt”. While this does not always imply that the variable does not exist in the dataset, it is a reasonable explanation.
  • As historical V-DEM is a separate project, there are certain inconsistencies that have been mentioned. This could be the reason why not all of these variables have been included so far.
  • Furthermore, there are series of dichotomous variables where only certain levels are missing. In the case of v2eltype, there is explicit information in the responses (see the Codebook as these have been replaced) that these levels have not been coded yet.

While these considerations certainly do not answer all the questions, I hope that the table will at least demonstrate that no simple coding errors have been made and that the variables are indeed not present in the dataset.

I will therefore now remove these variables.

tmp_mf_vdem <- filter(tmp_mf_vdem, name %in% names(tmp_vdem))

Add osp and ord

We now add the suffix “_osp" or “_ord" depending on whether the corresponding variable exists as a column in tmp_vdem. This operation implies changes in the number of rows. Furthermore we need to adapt the fxInfo_name column with an appropriate suffix:

name_suffix <- c(" (Relative)", osp = " (Original)", ord = " (Ordinal)")
tmp_mf_vdem <- 
  seq_len(nrow(tmp_mf_vdem)) %>% 
  map_dfr(
    function(i_row) {
      tmp_old <- tmp_mf_vdem[i_row, ]
      names <- paste0(tmp_old$name, c("", "_osp", "_ord"))
      if(names[2] %in% names(tmp_vdem)) {
        stopifnot(names[3] %in% names(tmp_vdem))
        ret <- tmp_old[rep(1, 3), ]
        ret <- ret %>% 
          mutate(
            name = names, 
            fxInfo_name = paste0(fxInfo_name, name_suffix)
          )
      }
      else ret <- tmp_old
      ret
    }
  )

Add codehigh, codelow, sd and nr

We now add the suffix “_codehigh“,”_codelow“,”_sd" and “_nr" depending on whether the corresponding variable exists. codehigh and codelow should exist together but sd and nr may be independent.

tmp_mf_vdem <-
  seq_len(nrow(tmp_mf_vdem)) %>% 
  map_dfr(
    function(i_row) {
      tmp_old <- tmp_mf_vdem[i_row, ]
      names <- paste0(tmp_old$name, c("", "_codehigh", "_codelow", "_sd", "_nr"))
      if(!any(names[2:5] %in% names(tmp_vdem))) return(tmp_old)
      ret <- tmp_old
      if(names[2] %in% names(tmp_vdem)) {
        stopifnot(names[3] %in% names(tmp_vdem))
        ret <- tmp_old[rep(1, 3), ]
        ret <- ret %>% 
          mutate(
            name = names[1:3],
            fxInfo_name = paste0(
              fxInfo_name,
              c("", " (Upper CI)", " (Lower CI)")
            )
          )
      }
      if(names[4] %in% names(tmp_vdem)) {
        ret <- bind_rows(
          ret, 
          tmp_old %>% 
            mutate(
              name = names[4], 
              fxInfo_name = paste0(fxInfo_name, " (Std. Dev.)")
            )
        )
      }
      if(names[5] %in% names(tmp_vdem)) {
        ret <- bind_rows(
          ret, 
          tmp_old %>% 
            mutate(
              name = names[5], 
              fxInfo_name = paste0(fxInfo_name, " (Nr of experts)")
            )
        )
      }
      ret
    }
  )
LS0tDQp0aXRsZTogIkNvZGVib29rIGFmdGVyIFdyaXRpbmciDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpgYGB7cn0NCmxpYnJhcnkodGVjdHIpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkoaGF2ZW4pDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeShnbHVlKQ0KZGV2dG9vbHM6OmxvYWRfYWxsKCkNCmBgYA0KDQojIE92ZXJ2aWV3DQoNCkJldHdlZW4gdGhlIGVuZCBvZiAiQ29kZWJvb2tfbWV0YWZyYW1lIiBhbmQgaGVyZSwgdGhlIG1ldGFmcmFtZSBtYXkgYmUgY2hhbmdlZCANCmJ5IGZvbGxvd2luZyB0aGUgcGF0aCB0byB0aGUgc3BlY2lmaWMganNvbiBmaWxlLiBOb3csIHRoZSBtZXRhZnJhbWUgYW5kIHRoZSBkYXRhDQp3aWxsIGJlIGNyZWF0ZWQuIFdlIHdpbGwgYmVnaW4gd2l0aCB0aGUgYmFzaWNzIG9mIHRoZSBtZXRhZnJhbWUsIGZvbGxvd2VkIGJ5IHRoZQ0KZGF0YSwgZm9sbG93ZWQgYnkgZnVydGhlciB0d2Vha2luZyBvZiB0aGUgbWV0YWZyYW1lLg0KDQojIE1ldGFmcmFtZQ0KDQojIyBJbXBvcnQNCg0KSSB3aWxsIG5vdyByZWFkIGBtZl9yZXZpc2FibGVgIHRvIGdldCB0aGUgZGF0YSBmcm9tIHRoZSBqc29uIGZpbGVzOg0KDQpgYGB7cn0NCnRtcF9tZl92ZGVtIDwtIGZ4X3JlYWQobWZfcmV2aXNhYmxlKQ0KdG1wX21mX3ZkZW0NCmBgYA0KDQpJIHJlYWQgaW4gdGhlIGRhdGEsIGFzIHdlbGw6DQoNCmBgYHtyfQ0KcGF0aCA8LSBzeXN0ZW0uZmlsZSgiaW5zdCIsICJleHRkYXRhIiwgIkNvdW50cnlfWWVhcl9WLURlbV9TVEFUQV92OCIsICJWLURlbS1DWS12OC5kdGEiLHBhY2thZ2UgPSAidmRlbS50ZWN0ciIpDQp0bXBfdmRlbSA8LSByZWFkX2R0YShwYXRoKQ0KdG1wX3ZkZW0NCnJtKHBhdGgpDQpgYGANCg0KIyMgSWRlbnRpZmllcnMNCg0KYGhhdmVuOjphc19mYWN0b3JgIGFsbG93cyB1cyB0byB0cmFuc2Zvcm0gdGhlIGFwcHJvcHJpYXRlIGNvbHVtbnMgaW50byBmYWN0b3JzIGJ5IGFwcGx5aW5nIHRoZSBmdW5jdGlvbiB0byBgdG1wX3ZkZW1gLiBIb3dldmVyLCB0aGlzIG1ldGhvZCBjYW5ub3QgZGlzdGluZ3Vpc2ggYmV0d2VlbiBub21pbmFsIGFuZCBvcmRpbmFsIHZhcmlhYmxlcyBhbmQgd2Ugd2lsbCB0aGVyZWZvcmUgaGF2ZSB0byB3YWl0IHdpdGggdGhpcy4gRmlyc3QsIEkgd2lsbCBhZGQgdGhlIGlkZW50aWZpZXIgdmFyaWFibGVzIHRvIHRoZSBtZXRhZnJhbWUuIEkgZGlkIG5vdCBhZGQgdGhlbSB0byB0aGUgbWV0YWZyYW1lIGJlZm9yZWhhbmQgYmVjYXVzZSB0aGVpciBmb3JtYXQgaXMgZGlmZmVyZW50IGFuZCB0aGUgaW5mb3JtYXRpb24gY29udGFpbmVkIGluIHRoZSBjb2RlYm9vayBtb3N0bHkgcmVmZXJzIHRvIG90aGVyIGRvY3VtZW50cy4gSXQgbWF5IHNlbnNpYmxlIHRvIGFkZCB0aGUgaW5mb3JtYXRpb24gaW4gYSBmdXR1cmUgdmVyc2lvbi4gVGhlIG9uZXMgdGhhdCBJIHdpbGwgZXhwbGljaXRseSBhZGQgZm9yIG5vdywgYXJlICJjb3VudHJ5X25hbWUiIGFuZCAieWVhciIuDQoNCmBgYHtyfQ0KdG1wX21mX3ZkZW0gPC0gYmluZF9yb3dzKHRpYmJsZShuYW1lID0gYygiY291bnRyeV9uYW1lIiwgInllYXIiKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ4SW5mb19uYW1lID0gYygiQ291bnRyeSBOYW1lIiwgIlllYXIiKSksIA0KICAgICAgICAgICAgICAgICAgICAgICAgIHRtcF9tZl92ZGVtKQ0KYGBgDQoNCk1hbnkgb2YgdGhlIGNvbHVtbnMgb2YgYHRtcF9tZl92ZGVtYCBhcmUgbm90IGltcG9ydGFudCBmb3IgdGhlIHRhc2sgb2YgZm9ybWF0dGluZyB0aGUgZGF0YS4gVGhlIGdvYWwgaXMsIGF0IGZpcnN0LCB0byBoYXZlIGEgbWV0YWZyYW1lIHdoaWNoIGNvbnRhaW5zIGFsbCBjb2x1bW5zIG9mIHRoZSBjb3JyZXNwb25kaW5nIGRhdGFzZXQgKHBvc3NpYmx5IG1vcmUpIGFuZCBhIGRhdGFmcmFtZSB3aGVyZSB0aGUgdmFsdWVzIGFyZSBjb3JyZWN0bHkgY29kZWQuIFdlIHRodXMgZmlyc3QgbG9vayBhdCB0aGUgY2xhc3Mgb2YgdGhlIGNvbHVtbnMgb2YgYHRtcF92ZGVtYDoNCg0KYGBge3J9DQp0bXBfdmRlbSAlPiUgDQogIG1hcF9jaHIoY2xhc3MpICU+JSANCiAgdGFibGUNCmBgYA0KDQpUaGUgb25seSB0YXNrIGlzIHRvIGRpZmZlcmVudGlhdGUgYmV0d2VlbiBvcmRpbmFsIGFuZCBub21pbmFsIHZhcmlhYmxlcyBhbmQgbWFrZSBzdXJlIHRoYXQgYWxsIHZhcmlhYmxlcyBidXQgdGhlIGlkZW50aWZpZXJzIGFyZSBkZXNjcmliZWQgaW4gdGhlIG1mX3ZkZW0gZGF0YS4gVGhlIGlkZW50aWZpZXJzIGFyZTogDQoNCmBgYHtyfQ0KbmFtZXModG1wX3ZkZW0pWzE6MjFdDQpgYGANCg0KT25seSAiY291bnRyeV9uYW1lIiBhbmQgInllYXIiIGFyZSBjb250YWluZWQgaW4gdGhlIG1ldGFmcmFtZS4NCg0KYGBge3J9DQppZGVudHMgPC0gbmFtZXModG1wX3ZkZW0pW2MoMiwgMywgNToyMSldDQpgYGANCg0KSWYgd2UgcmVtb3ZlIHRoZXNlIHZhcmlhYmxlcywgd2UgZ2V0Og0KDQpgYGB7cn0NCnRtcF92ZGVtICU+JSANCiAgc2VsZWN0KC0hIWlkZW50cykgJT4lIA0KICBtYXBfY2hyKGNsYXNzKSAlPiUgDQogIHRhYmxlDQpgYGANCg0KIyMgUHJlbGltaW5hcmllcw0KDQpMZXQncyB0YWtlIGEgcGVlayBhdCB0aGUgY29sdW1uIG5hbWVzIHRoYXQgYXJlIG5vdCB5ZXQgY29udGFpbmVkOg0KDQpgYGB7cn0NCm5hbWVzKHRtcF92ZGVtICU+JSBzZWxlY3QoLSEhaWRlbnRzKSkgJT4lIA0KICBleHRyYWN0KCEoLiAlaW4lIHRtcF9tZl92ZGVtJG5hbWUpKSAlPiUgDQogIGxlbmd0aA0KYGBgDQoNClRoZXJlIGFyZSB0aHJlZSBtYWluIHJlYXNvbnMgd2h5IHNvIG1hbnkgdmFyaWFibGVzIGFyZSBub3QgY29udGFpbmVkIGluIHRoZSBtZXRhZnJhbWU6DQoNCiogdGhlcmUgYXJlIHNlcmllcyBvZiBkaWNob3RvbW91cyB2YXJpYWJsZXMgdGhhdCBhcmUgZW5jb2RlZCBieSAiPG5hbWU+XzxsZXZlbD4iIGZvciBlYWNoIGxldmVsLCBlLiBnLiwgdjJjc2FubXZjaDoNCg0KYGBge3J9DQpuYW1lcyh0bXBfdmRlbSkgJT4lIHN0cl9zdWJzZXQoY29sbCgidjJjc2FubXZjaCIpKQ0KYGBgDQoNCiogbWFueSBuYW1lcyBhcmUgc2F2ZWQgaW4gdGhlIGZvcm0gIjxuYW1lPiwgXCpfb3NwLCBcKl9vcmQiDQoqIHRoZXJlIGlzIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gb24gbWFueSB2YXJpYWJsZXMgaW4gdGhlIGZvcm0gb2YgY29uZmlkZW5jZSBpbnRlcnZhbHMgKCJfY29kZWhpZ2giLyJfY29kZWxvdyIpLCBzdGFuZGFyZCBkZXZpYXRpb25zICgiX3NkIikgYW5kIHRoZSBudW1iZXIgb2YgZXhwZXJ0cyB3aG8gY29kZWQgdGhlbQ0KDQpBcyB0aGlzIGlzIHBhcnRseSBzdGFja2VkIGluIHRoZSBkaXJlY3Rpb24gaW4gd2hpY2ggSSBoYXZlIGxpc3RlZCBpdCwgd2Ugd2lsbCBkZWNvZGUgaXQgaW4gdGhpcyBkaXJlY3Rpb24uIEhvd2V2ZXIsIHRoZXJlIGFyZSB0d28gcHJldmlvdXMgdGFza3MgdG8gZnVsZmlsbDoNCg0KIyMjIENvbHVtbiBuYW1lIGNvcnJlY3Rpb25zDQoNClRoZXJlIGFyZSBhIGZldyB2YXJpYWJsZXMgdGhhdCBhcmUgaW5jb25zaXN0ZW50bHkgbmFtZWQuDQoNCmBgYHtyfQ0KbmFtZXModG1wX3ZkZW0pICU+JSBzdHJfc3Vic2V0KCJvc3BfZXgiKQ0KYGBgDQoNClRoZSB2YXJpYWJsZSBuYW1lIGhlcmUgaXMgIjxuYW1lPl9leCIgYW5kIGEgbW9yZSBjb25zaXN0ZW50IG5hbWUgd291bGQgdGhlcmVmb3JlIGJlICI8bmFtZT5fZXhfb3NwIi4gTGV0IHVzIGxvb2sgYXQgdGhlIGRpZmZlcmVudCBmb3JtcyBpbiB3aGljaCB0aGVzZSBpbmNvbnNpc3RlbnQgdmFyaWFibGVzIGNvbWU6DQoNCmBgYHtyfQ0KbmFtZXModG1wX3ZkZW0pICU+JSBzdHJfc3Vic2V0KCJ2MmVsbXVscGFyX1xcUypfKD86ZXh8bGVnKSIpDQpgYGANCg0KV2UgdGhlcmVmb3JlIGRlZmluZSBhIGZ1bmN0aW9uIHdoaWNoIGNvcnJlY3RzIHRoZXNlIG5hbWVzIGJ5IHN3aXRjaGluZyB0aGUgcGF0dGVybiB3aXRoIHRoZSBzdWZmaXg6DQoNCmBgYHtyfQ0KY29ycmVjdF9uYW1lcyA8LSBmdW5jdGlvbihuYW1lcykgew0KICAjIE9uZSBvZiB0aGUgZm9sbG93aW5nIG1peHR1cmVzIGhhcyB0byBleGlzdCB0aGF0IGlzIG5vdCBlbXB0eToNCiAgZ3JpZCA8LSBleHBhbmQuZ3JpZCgNCiAgICBjKCJfb3NwIiwgIl9vcmQiLCAiIiksDQogICAgYygiX2NvZGVoaWdoIiwgIl9jb2RlbG93IiwgIl9zZCIsICIiKQ0KICApDQogIHByb3Blcl9lbmQgPC0gcGFzdGUwKGdyaWRbWzFdXSwgZ3JpZFtbMl1dKSAlPiUNCiAgICBtYWdyaXR0cjo6ZXh0cmFjdCguICE9ICIiKSAlPiUNCiAgICBwYXN0ZShjb2xsYXBzZSA9ICJ8IikNCiAgdW5wcm9ibGVtYXRpY19wYXR0ZXJuIDwtIGdsdWU6OmdsdWUoIig/Ontwcm9wZXJfZW5kfSkkIikNCiAgbG9uZ19ncmlkIDwtIGV4cGFuZC5ncmlkKA0KICAgIGMoIl9vc3AiLCAiX29yZCIpLA0KICAgIGMoIl9jb2RlaGlnaCIsICJfY29kZWxvdyIsICJfc2QiKQ0KICApDQogIGxvbmdfcHJvcGVyX2VuZCA8LSBwYXN0ZTAobG9uZ19ncmlkW1sxXV0sIGxvbmdfZ3JpZFtbMl1dLCBjb2xsYXBzZSA9ICJ8IikNCiAgc2hvcnRfcHJvcGVyX2VuZCA8LSBwYXN0ZTAoYygiX29zcCIsICJfb3JkIiwgIl9jb2RlaGlnaCIsICJfY29kZWxvdyIsICJfc2QiKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbGxhcHNlID0gInwiKQ0KICBuYW1lcyA8LSBuYW1lcyAlPiUgew0KICAgIGlmX2Vsc2UoDQogICAgICBzdHJfZGV0ZWN0KC4sIHVucHJvYmxlbWF0aWNfcGF0dGVybiksIC4sIA0KICAgICAgc3RyX3JlcGxhY2UoLiwgZ2x1ZSgNCiAgICAgICAgIig8bG9uZ19wcm9wZXJfZW5kPikoX1s6YWxwaGE6XXsxLDUwfSkkIiwgLm9wZW4gPSAiPCIsIC5jbG9zZSA9ICI+Ig0KICAgICAgICApLCAiXFwyXFwxIikNCiAgICApDQogIH0gJT4lIHsNCiAgICBpZl9lbHNlKA0KICAgICAgc3RyX2RldGVjdCguLCB1bnByb2JsZW1hdGljX3BhdHRlcm4pLCAuLCANCiAgICAgIHN0cl9yZXBsYWNlKC4sIGdsdWUoDQogICAgICAgICIoPHNob3J0X3Byb3Blcl9lbmQ+KShfWzphbHBoYTpdezEsNTB9KSQiLCAub3BlbiA9ICI8IiwgLmNsb3NlID0gIj4iDQogICAgICAgICksICJcXDJcXDEiKQ0KICAgICkNCiAgfQ0KICBuYW1lcw0KfQ0KYGBgDQoNCkkgaGF2ZSBpbmNsdWRlZCBhIHNob3J0IGV4YW1wbGUgd2l0aCBkaWZmZXJlbnQgZmxhdm9ycyBvZiBjb3JyZWN0IGFuZCBpbmNvcnJlY3QgbmFtZXM6DQoNCmBgYHtyfQ0KZXggPC0gYygNCiAgICAidjJwc3ByYnJjaF9vcmRfY29kZWhpZ2giLA0KICAgICJ2MmVsYXNtb2ZmX2NvZGVsb3dfZXgiLA0KICAgICJ2MmVsYXNtb2ZmIiwNCiAgICAidjJlbGFzbW9mZl9vcmRfY29kZWxvd19leCIsDQogICAgInYyZWxhc21vZmZfZXgiLCANCiAgICAidjJlbHBlYWNlX3JlY19jb2RlbG93X2V4Ig0KICApDQpjb3JyZWN0X25hbWVzKGV4KQ0KYGBgDQoNCldlIG5vdyBjaGFuZ2UgYHRtcF92ZGVtYDoNCg0KYGBge3J9DQpuYW1lcyh0bXBfdmRlbSkgPC0gY29ycmVjdF9uYW1lcyhuYW1lcyh0bXBfdmRlbSkpDQpgYGANCg0KYGBge3J9DQpuYW1lcyh0bXBfdmRlbSkgJT4lIHN0cl9zdWJzZXQoIm9zcF9leCIpDQpgYGANCg0KDQojIyMgTWV0YWZyYW1lIG5hbWUgY29ycmVjdGlvbnMNCg0KVGhlcmUgYXJlIHR3byByZWFzb25zIHdoeSBuYW1lcyBhcmUgaW5jb3JyZWN0IGluIHRoZSBtZXRhZnJhbWU6IHRoZXJlIGlzIHRoZSBwYXR0ZXJuICI8bmFtZT4sICpfb3NwIC4uLiIgYW5kICI8bmFtZT5fM0MvXzRDIC4uLiIgDQoNCldoZXJlYXMgd2Ugb25seSBkZWxldGUgIiwgKl9vc3AgLi4uIiwgdGhlIHNlY29uZCBwYXR0ZXJuIGFjdHVhbGx5IGltcGxpZXMgc2V2ZXJhbCB2YXJpYWJsZXMgYW5kIHdlIHRoZXJlZm9yZSBoYXZlIHRvIGNvbmR1Y3QgYW4gaW5uZXIgam9pbiAodG9nZXRoZXIgd2l0aCBzb21lIG1vcmUgZmxleGlibGUgcmVndWxhciBleHByZXNzaW9ucyBiZWNhdXNlIG9mIHR5cG9zKToNCg0KYGBge3J9DQprZXkgPC0gZHBseXI6OnRpYmJsZSgNCiAgICBiZWZvcmUgPSB0bXBfbWZfdmRlbSRuYW1lLA0KICAgIG5hbWUgPSB0bXBfbWZfdmRlbSRuYW1lICU+JQ0KICAgICAgcHVycnI6Om1hcCgNCiAgICAgIGZ1bmN0aW9uKG5hbWUpIHsNCiAgICAgICAgaWYoc3RyaW5ncjo6c3RyX2RldGVjdChuYW1lLCAiXFwqX29zcCwiKSkgew0KICAgICAgICAgIHN0cmluZ3I6OnN0cl9leHRyYWN0KG5hbWUsICJeW15cXHMqXSooPz0sKSIpICU+JQ0KICAgICAgICAgICAgcmV0dXJuKCkNCiAgICAgICAgfQ0KICAgICAgICBlbHNlIGlmKHN0cmluZ3I6OnN0cl9kZXRlY3QobmFtZSwgIl8zQ1xcc1xcLyIpKSB7DQogICAgICAgICAgc3RyaW5ncjo6c3RyX2V4dHJhY3QobmFtZSwgIl5cXFMqKD89XzNDKSIpICU+JQ0KICAgICAgICAgICAgcGFzdGUwKGMoIl8zQyIsICJfNEMiLCAiXzVDIikpICU+JQ0KICAgICAgICAgICAgcmV0dXJuKCkNCiAgICAgICAgfQ0KICAgICAgICBlbHNlIHJldHVybihuYW1lKQ0KICAgICAgfQ0KICAgICkNCiAgKSAlPiUgdGlkeXI6OnVubmVzdCgpDQp0bXBfbWZfdmRlbSA8LSANCiAgZHBseXI6OmlubmVyX2pvaW4oa2V5LCB0bXBfbWZfdmRlbSwgYnkgPSBjKGJlZm9yZSA9ICJuYW1lIikpICU+JQ0KICBkcGx5cjo6c2VsZWN0KC1iZWZvcmUpDQpgYGANCg0KIyMgU3BsaXQgdGhlIHNlcmllcyBvZiBkaWNob3RvbW91cyB2YXJpYWJsZXMNCg0KQmVzaWRlcyBleHRlbmRpbmcgdGhlIG5hbWVzLCB0aGUgZm9sbG93aW5nIGNvZGUgZWRpdHMgdGhlIHF1ZXN0aW9uIGFuZCBhbnN3ZXIgb2YgdGhlIG5ldyBkaWNob3RvbW91cyB2YXJpYWJsZSBzbyB0aGF0IHRoZXkgYXJlIG1vcmUgYXBwcm9wcmlhdGUuICBCZWNhdXNlIG9mIHNvbWUgaW5jb25zaXN0ZW5jaWVzIGluIHRoZSBjb2RlYm9vaywgSSBoYWQgdG8gdHJ5IG91dCBzb21lIGRpZmZlcmVudCBjcml0ZXJpYSBmb3IgZGljaG90b21vdXMgdmFyaWFibGVzLiBUaGUgZm9sbG93aW5nIG9uZXMgdHVybmVkIG91dCB0byBiZSBhcHByb3ByaWF0ZToNCg0KYGBge3J9DQppc19kcyA8LSB0bXBfbWZfdmRlbSAlJCUNCiAgeygoc3RyaW5ncjo6c3RyX2RldGVjdChmeEluZm9fYW5zd2VyX3R5cGUsICIoPzpTfHMpZWxlY3Rpb24iKSAmDQogICAgIXN0cmluZ3I6OnN0cl9kZXRlY3QoZnhJbmZvX3NjYWxlLCAiT3JkaW5hbCIpKSB8DQogICAgc3RyaW5ncjo6c3RyX2RldGVjdChmeEluZm9fc2NhbGUsICIoPzpTfHMpZXJpZXMiKSkgJiANCiAgICAhKG5hbWUgJWluJSBjKCJjb3VudHJ5X25hbWUiLCAieWVhciIsICJ2MmV4cGF0aGhnIikpfQ0KbWZfcmVtIDwtIHRtcF9tZl92ZGVtWyFpc19kcywgXQ0KbWZfZGljaCA8LSB0bXBfbWZfdmRlbVtpc19kcywgXQ0KbWZfZGljaF9uZXcgPC0gc2VxX2xlbihucm93KG1mX2RpY2gpKSAlPiUNCiAgcHVycnI6Om1hcF9kZnIoDQogICAgZnVuY3Rpb24oaV9yb3cpIHsNCiAgICAgIHRtcF9vbGQgPC0gbWZfZGljaFtpX3JvdywgXQ0KICAgICAgcmVzcHMgPC0gdG1wX29sZCRmeEluZm9fcmVzcG9uc2VzW1sxXV0gJT4lDQogICAgICAgIGRwbHlyOjphc190aWJibGUoKSAlPiUNCiAgICAgICAgZHBseXI6Om11dGF0ZSgNCiAgICAgICAgICB2YWx1ZSA9IHN0cmluZ3I6OnN0cl9zcGxpdF9maXhlZCh2YWx1ZSwNCiAgICAgICAgICAgICAgcGF0dGVybiA9IHN0cmluZ3I6OmNvbGwoIigwPU5vLCAxPVllcykiKSwNCiAgICAgICAgICAgICAgbiA9IDIpWywgMV0gJT4lIHN0cmluZ3I6OnN0cl90cmltKCkNCiAgICAgICAgKQ0KICAgICAgcXVlc3Rpb24gPC0gdG1wX29sZCRmeEluZm9fcXVlc3Rpb24NCiAgICAgIHF1ZXN0aW9uX25ldyA8LSBnbHVlOjpnbHVlKA0KICAgICAgICBxdWVzdGlvbiwgIiBJcyB0aGUgYW5zd2VyIFwie3Jlc3BzJHZhbHVlfVwiPyINCiAgICAgICkgJT4lIGFzLmNoYXJhY3RlcigpDQogICAgICByZXNwb25zZV9uZXcgPC0gIlllcyBvciBObyINCiAgICAgIG5hbWVfbmV3IDwtIHBhc3RlMCh0bXBfb2xkJG5hbWUsICJfIiwgcmVzcHMka2V5KQ0KICAgICAgdG1wX29sZCA8LSB0bXBfb2xkW3JlcCgxLCBucm93KHJlc3BzKSksIF0NCiAgICAgIHRtcF9vbGQgJT4lDQogICAgICAgIGRwbHlyOjptdXRhdGUoDQogICAgICAgICAgbmFtZSA9IG5hbWVfbmV3LA0KICAgICAgICAgIGZ4SW5mb19xdWVzdGlvbiA9IHF1ZXN0aW9uX25ldywNCiAgICAgICAgICBmeEluZm9fcmVzcG9uc2VzID0gbGlzdChyZXNwb25zZV9uZXcpDQogICAgICAgICkNCiAgICB9DQogICkNCnRtcF9tZl92ZGVtIDwtIGRwbHlyOjpiaW5kX3Jvd3MobWZfcmVtLCBtZl9kaWNoX25ldykNCmBgYA0KDQojIyBPbmx5IGtlZXAgdmFyaWFibGVzIHRoYXQgY2FuIGJlIGZvdW5kIGluIGB0bXBfdmRlbWANCg0KV2hpbGUgdGhlIG1ldGFmcmFtZSBtaWdodCBiZSBleHRlbmRlZCBpbiBmdXR1cmUgdmVyc2lvbnMsIGZvciBub3csIGl0IHNlZW1zIHNlbnNpYmxlIHRvIHJldGFpbiBvbmx5IHRoZSBjb2x1bW5zIHRoYXQgY2FuIGJlIGZvdW5kIGluIGB0bXBfdmRlbWAuIFRvIG1ha2Ugc3VyZSB0aGF0IHdlIGRpZCBub3Qgb3Zlcmxvb2sgYW55dGhpbmcsIHdlIHdpbGwgZG8gdGhpcyBncmFkdWFsbHkuDQoNCkZpcnN0IG9mIGFsbCwgdGhvc2UgdmFyaWFibGVzIG9mIHRoYXQgYXJlIGZvdW5kIGluIHRoZSBmb3VydGggYW5kIGZpZnRoIHBhcnQgYXJlIG9ubHkgcHJlc2VudCBpbiB0aGUgZXh0ZW5kZWQgZGF0YXNldDoNCg0KYGBge3J9DQp0bXBfbWZfdmRlbSAlPiUNCiAgZmlsdGVyKHBhcnRfbnVtID49IDQpICUkJSANCiAgbmFtZSAlaW4lIG5hbWVzKHRtcF92ZGVtKSAlPiUgDQogIGFueQ0KYGBgDQoNCldlIHRoZXJlZm9yZSByZW1vdmUgdGhlbToNCg0KYGBge3J9DQp0bXBfbWZfdmRlbSA8LSB0bXBfbWZfdmRlbSAlPiUgDQogIGZpbHRlcihwYXJ0X251bSA8IDQpDQpgYGANCg0KRnVydGhlcm1vcmUsIG1hbnkgc2VjdGlvbnMgY29udGFpbiBpbnRyb2R1Y3Rpb24gd2hpY2gsIGFnYWluLCBtaWdodCBiZSBpbnRlcmVzdGluZyBpbiB0aGUgZnV0dXJlIGJ1dCB3aGljaCB3ZSB3aWxsIHJlbW92ZSBmb3Igbm93Og0KDQpgYGB7cn0NCnRtcF9tZl92ZGVtICU+JQ0KICBmaWx0ZXIoc3RyX2RldGVjdChuYW1lLCAiaW50cm8iKSB8IHN0cl9kZXRlY3QoZnhJbmZvX25hbWUsICJjb21tZW50IikpICUkJSANCiAgbmFtZSAlaW4lIG5hbWVzKHRtcF92ZGVtKSAlPiUgDQogIGFueQ0KYGBgDQoNCmBgYHtyfQ0KdG1wX21mX3ZkZW0gPC0gdG1wX21mX3ZkZW0gJT4lIA0KICBmaWx0ZXIoIXN0cl9kZXRlY3QobmFtZSwgImludHJvIikgJiAhc3RyX2RldGVjdChmeEluZm9fbmFtZSwgImNvbW1lbnQiKSkNCmBgYA0KDQpTb21lIHZhcmlhYmxlcyBjYW4gb25seSBiZSBmb3VuZCBpbiB0aGUgZGlzYWdncmVnYXRlZCBkYXRhc2V0Og0KDQpgYGB7cn0NCnRtcF9tZl92ZGVtICU+JQ0KICBmaWx0ZXIoc3RyX2RldGVjdChmeEluZm9fZGF0YV9yZWxlYXNlLCAiZGlzYWdncmVnYXRlZCBkYXRhc2V0IikpICUkJSANCiAgbmFtZSAlaW4lIG5hbWVzKHRtcF92ZGVtKSAlPiUgDQogIGFueQ0KYGBgDQoNCmBgYHtyfQ0KdG1wX21mX3ZkZW0gPC0gdG1wX21mX3ZkZW0gJT4lDQogIGZpbHRlcighc3RyX2RldGVjdChmeEluZm9fZGF0YV9yZWxlYXNlLCAiZGlzYWdncmVnYXRlZCBkYXRhc2V0IikpDQpgYGANCg0KRnVydGhlcm1vcmUsIHRoZSBjYXV0aW9uYXJ5IG5vdGVzIHJlcG9ydCBzb21lIHZhcmlhYmxlcyB0aGF0IGhhdmUgYmVlbiBleGNsdWRlZDoNCg0KYGBge3J9DQp0bXBfbWZfdmRlbSA8LSB0bXBfbWZfdmRlbSAlPiUgDQogIGZpbHRlcighKG5hbWUgJWluJSBjKA0KICAgICJ2MmVsbm9uY2l0IiwgInYyZWxtYWxzdWYiLCAidjJlbGZlbXN1ZiIsICJ2MmVsbWFsc3VmX2V4IiwgInYyZWxmZW1zdWZfZXgiLA0KICAgICJ2MmVsbWFsc3VmX2xlZyIsICJ2MmVsZmVtc3VmX2xlZyIsICJ2MmVsc25scG9wIiwgInYyZWxzbm1wb3AiLCANCiAgICAidjJwc3N3aXRjaCIsICJ2MmNsc25tcGN0IiwgInYyc3ZzdHRlcnIiLCAidjJzdnN0cG9wIiwgInYybWVhY2Nlc3MiLCANCiAgICAidjJsZ3F1bWluIg0KICApKSkNCmBgYA0KDQpXZSB3aWxsIGluc3BlY3QgdGhlIHJlbWFpbmluZyB2YXJpYWJsZXMgdGhhdCBkbyBub3QgZXhpc3QsIG1hbnVhbGx5Og0KDQpgYGB7cn0NCnByaW50KGZpbHRlcih0bXBfbWZfdmRlbSwgIShuYW1lICVpbiUgbmFtZXModG1wX3ZkZW0pKSksIG4gPSA0MCkNCmBgYA0KDQoqIFRoZSB2YXJpYWJsZXMgZnJvbSB0aGUgY29udGVtcG9yYXJ5IFYtREVNIChiZWdpbm5pbmcgd2l0aCB2MikgaGF2ZSBwcmV2aW91cyBkYXRhIHJlbGVhc2VzIGFzc29jaWF0ZWQgd2l0aCB0aGVtLCBleGNlcHQgZm9yICJ2MmVsYWRsdHZ0Ii4gV2hpbGUgdGhpcyBkb2VzIG5vdCBhbHdheXMgaW1wbHkgdGhhdCB0aGUgdmFyaWFibGUgZG9lcyBub3QgZXhpc3QgaW4gdGhlIGRhdGFzZXQsIGl0IGlzIGEgcmVhc29uYWJsZSBleHBsYW5hdGlvbi4gDQoqIEFzIGhpc3RvcmljYWwgVi1ERU0gaXMgYSBzZXBhcmF0ZSBwcm9qZWN0LCB0aGVyZSBhcmUgY2VydGFpbiBpbmNvbnNpc3RlbmNpZXMgdGhhdCBoYXZlIGJlZW4gbWVudGlvbmVkLiBUaGlzIGNvdWxkIGJlIHRoZSByZWFzb24gd2h5IG5vdCBhbGwgb2YgdGhlc2UgdmFyaWFibGVzIGhhdmUgYmVlbiBpbmNsdWRlZCBzbyBmYXIuDQoqIEZ1cnRoZXJtb3JlLCB0aGVyZSBhcmUgc2VyaWVzIG9mIGRpY2hvdG9tb3VzIHZhcmlhYmxlcyB3aGVyZSBvbmx5IGNlcnRhaW4gbGV2ZWxzIGFyZSBtaXNzaW5nLiBJbiB0aGUgY2FzZSBvZiB2MmVsdHlwZSwgdGhlcmUgaXMgZXhwbGljaXQgaW5mb3JtYXRpb24gaW4gdGhlIHJlc3BvbnNlcyAoc2VlIHRoZSBDb2RlYm9vayBhcyB0aGVzZSBoYXZlIGJlZW4gcmVwbGFjZWQpIHRoYXQgdGhlc2UgbGV2ZWxzIGhhdmUgbm90IGJlZW4gY29kZWQgeWV0Lg0KDQpXaGlsZSB0aGVzZSBjb25zaWRlcmF0aW9ucyBjZXJ0YWlubHkgZG8gbm90IGFuc3dlciBhbGwgdGhlIHF1ZXN0aW9ucywgSSBob3BlIHRoYXQgdGhlIHRhYmxlIHdpbGwgYXQgbGVhc3QgZGVtb25zdHJhdGUgdGhhdCBubyBzaW1wbGUgY29kaW5nIGVycm9ycyBoYXZlIGJlZW4gbWFkZSBhbmQgdGhhdCB0aGUgdmFyaWFibGVzIGFyZSBpbmRlZWQgbm90IHByZXNlbnQgaW4gdGhlIGRhdGFzZXQuDQoNCkkgd2lsbCB0aGVyZWZvcmUgbm93IHJlbW92ZSB0aGVzZSB2YXJpYWJsZXMuDQoNCmBgYHtyfQ0KdG1wX21mX3ZkZW0gPC0gZmlsdGVyKHRtcF9tZl92ZGVtLCBuYW1lICVpbiUgbmFtZXModG1wX3ZkZW0pKQ0KYGBgDQoNCg0KIyMgQWRkIG9zcCBhbmQgb3JkDQoNCldlIG5vdyBhZGQgdGhlIHN1ZmZpeCAiX29zcCIgb3IgIl9vcmQiIGRlcGVuZGluZyBvbiB3aGV0aGVyIHRoZSBjb3JyZXNwb25kaW5nIHZhcmlhYmxlIGV4aXN0cyBhcyBhIGNvbHVtbiBpbiBgdG1wX3ZkZW1gLiBUaGlzIG9wZXJhdGlvbiBpbXBsaWVzIGNoYW5nZXMgaW4gdGhlIG51bWJlciBvZiByb3dzLiBGdXJ0aGVybW9yZSB3ZSBuZWVkIHRvIGFkYXB0IHRoZSBgZnhJbmZvX25hbWVgIGNvbHVtbiB3aXRoIGFuIGFwcHJvcHJpYXRlIHN1ZmZpeDoNCg0KYGBge3J9DQpuYW1lX3N1ZmZpeCA8LSBjKCIgKFJlbGF0aXZlKSIsIG9zcCA9ICIgKE9yaWdpbmFsKSIsIG9yZCA9ICIgKE9yZGluYWwpIikNCmBgYA0KDQpgYGB7cn0NCnRtcF9tZl92ZGVtIDwtIA0KICBzZXFfbGVuKG5yb3codG1wX21mX3ZkZW0pKSAlPiUgDQogIG1hcF9kZnIoDQogICAgZnVuY3Rpb24oaV9yb3cpIHsNCiAgICAgIHRtcF9vbGQgPC0gdG1wX21mX3ZkZW1baV9yb3csIF0NCiAgICAgIG5hbWVzIDwtIHBhc3RlMCh0bXBfb2xkJG5hbWUsIGMoIiIsICJfb3NwIiwgIl9vcmQiKSkNCiAgICAgIGlmKG5hbWVzWzJdICVpbiUgbmFtZXModG1wX3ZkZW0pKSB7DQogICAgICAgIHN0b3BpZm5vdChuYW1lc1szXSAlaW4lIG5hbWVzKHRtcF92ZGVtKSkNCiAgICAgICAgcmV0IDwtIHRtcF9vbGRbcmVwKDEsIDMpLCBdDQogICAgICAgIHJldCA8LSByZXQgJT4lIA0KICAgICAgICAgIG11dGF0ZSgNCiAgICAgICAgICAgIG5hbWUgPSBuYW1lcywgDQogICAgICAgICAgICBmeEluZm9fbmFtZSA9IHBhc3RlMChmeEluZm9fbmFtZSwgbmFtZV9zdWZmaXgpDQogICAgICAgICAgKQ0KICAgICAgfQ0KICAgICAgZWxzZSByZXQgPC0gdG1wX29sZA0KICAgICAgcmV0DQogICAgfQ0KICApDQpgYGANCg0KIyMgQWRkIGBjb2RlaGlnaGAsIGBjb2RlbG93YCwgYHNkYCBhbmQgYG5yYA0KDQpXZSBub3cgYWRkIHRoZSBzdWZmaXggIl9jb2RlaGlnaCIsICJfY29kZWxvdyIsICJfc2QiIGFuZCAiX25yIiBkZXBlbmRpbmcgb24gd2hldGhlciB0aGUgY29ycmVzcG9uZGluZyB2YXJpYWJsZSBleGlzdHMuIGNvZGVoaWdoIGFuZCBjb2RlbG93IHNob3VsZCBleGlzdCB0b2dldGhlciBidXQgc2QgYW5kIG5yIG1heSBiZSBpbmRlcGVuZGVudC4NCg0KYGBge3J9DQp0bXBfbWZfdmRlbSA8LQ0KICBzZXFfbGVuKG5yb3codG1wX21mX3ZkZW0pKSAlPiUgDQogIG1hcF9kZnIoDQogICAgZnVuY3Rpb24oaV9yb3cpIHsNCiAgICAgIHRtcF9vbGQgPC0gdG1wX21mX3ZkZW1baV9yb3csIF0NCiAgICAgIG5hbWVzIDwtIHBhc3RlMCh0bXBfb2xkJG5hbWUsIGMoIiIsICJfY29kZWhpZ2giLCAiX2NvZGVsb3ciLCAiX3NkIiwgIl9uciIpKQ0KICAgICAgaWYoIWFueShuYW1lc1syOjVdICVpbiUgbmFtZXModG1wX3ZkZW0pKSkgcmV0dXJuKHRtcF9vbGQpDQogICAgICByZXQgPC0gdG1wX29sZA0KICAgICAgaWYobmFtZXNbMl0gJWluJSBuYW1lcyh0bXBfdmRlbSkpIHsNCiAgICAgICAgc3RvcGlmbm90KG5hbWVzWzNdICVpbiUgbmFtZXModG1wX3ZkZW0pKQ0KICAgICAgICByZXQgPC0gdG1wX29sZFtyZXAoMSwgMyksIF0NCiAgICAgICAgcmV0IDwtIHJldCAlPiUgDQogICAgICAgICAgbXV0YXRlKA0KICAgICAgICAgICAgbmFtZSA9IG5hbWVzWzE6M10sDQogICAgICAgICAgICBmeEluZm9fbmFtZSA9IHBhc3RlMCgNCiAgICAgICAgICAgICAgZnhJbmZvX25hbWUsDQogICAgICAgICAgICAgIGMoIiIsICIgKFVwcGVyIENJKSIsICIgKExvd2VyIENJKSIpDQogICAgICAgICAgICApDQogICAgICAgICAgKQ0KICAgICAgfQ0KICAgICAgaWYobmFtZXNbNF0gJWluJSBuYW1lcyh0bXBfdmRlbSkpIHsNCiAgICAgICAgcmV0IDwtIGJpbmRfcm93cygNCiAgICAgICAgICByZXQsIA0KICAgICAgICAgIHRtcF9vbGQgJT4lIA0KICAgICAgICAgICAgbXV0YXRlKA0KICAgICAgICAgICAgICBuYW1lID0gbmFtZXNbNF0sIA0KICAgICAgICAgICAgICBmeEluZm9fbmFtZSA9IHBhc3RlMChmeEluZm9fbmFtZSwgIiAoU3RkLiBEZXYuKSIpDQogICAgICAgICAgICApDQogICAgICAgICkNCiAgICAgIH0NCiAgICAgIGlmKG5hbWVzWzVdICVpbiUgbmFtZXModG1wX3ZkZW0pKSB7DQogICAgICAgIHJldCA8LSBiaW5kX3Jvd3MoDQogICAgICAgICAgcmV0LCANCiAgICAgICAgICB0bXBfb2xkICU+JSANCiAgICAgICAgICAgIG11dGF0ZSgNCiAgICAgICAgICAgICAgbmFtZSA9IG5hbWVzWzVdLCANCiAgICAgICAgICAgICAgZnhJbmZvX25hbWUgPSBwYXN0ZTAoZnhJbmZvX25hbWUsICIgKE5yIG9mIGV4cGVydHMpIikNCiAgICAgICAgICAgICkNCiAgICAgICAgKQ0KICAgICAgfQ0KICAgICAgcmV0DQogICAgfQ0KICApDQpgYGANCg0K